﻿@page "/pages/authentication/login"
@using Microsoft.AspNetCore.Authentication
@using CleanArchitecture.Blazor.Domain.Identity
@using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.SendFactorCode
@using System.ComponentModel.DataAnnotations

@inject ILogger<Login> Logger
@inject UserManager<ApplicationUser> UserManager
@inject IStringLocalizer<Login> L
@inject SignInManager<ApplicationUser> SignInManager
@inject IdentityRedirectManager RedirectManager

<PageTitle>@_title</PageTitle>

<div class="d-flex flex-column gap-y-3">
    <div class="d-flex flex-column">
        <EditForm Model="Input" method="post" OnValidSubmit="OnSubmit" FormName="login" Enhance>
            <DataAnnotationsValidator/>
            <MudText Typo="Typo.h4" GutterBottom="true">@L["Sign In"]</MudText>
            <MudText>
                @L["Don't have an account?"] <MudLink Href="@Register.PageUrl" Target="_self">@L["Sign Up"]</MudLink>
            </MudText>
            <StatusMessage Message="@errorMessage" Error="true"/>
            <div class="mud-input-control mud-input-input-control my-4">
                <div class="mud-input-control-input-container">
                    <!--!--><!--!-->
                    <div class="mud-input mud-input-outlined mud-shrink">
                        <InputText @bind-Value="Input.UserName" class="mud-input-slot mud-input-root mud-input-root-outlined" type="text" autocomplete="username" aria-required="true" placeholder="user name"/>
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined" style="display:none"></div>
                        <!--!-->
                        <div class="mud-input-outlined-border"></div>
                    </div>
                    <!--!-->
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="userName">@L["User name"]</label>
                </div>
                <div class="mud-input-helper-text mud-input-error">
                    <div class="d-flex">
                        <ValidationMessage For="() => Input.UserName" class="mud-input-error"/>
                    </div>
                </div>
            </div>
            <div class="mud-input-control mud-input-input-control my-4">
                <div class="mud-input-control-input-container">
                    <!--!--><!--!-->
                    <div class="mud-input mud-input-outlined mud-shrink">
                        <InputText type="password" @bind-Value="Input.Password" class="mud-input-slot mud-input-root mud-input-root-outlined" autocomplete="current-password" aria-required="true" placeholder="password"/>
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined" style="display:none"></div>
                        <!--!-->
                        <div class="mud-input-outlined-border"></div>
                    </div>
                    <!--!-->
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="password">@L["Password"]</label>
                </div>
                <div class="mud-input-helper-text mud-input-error">
                    <div class="d-flex">
                        <ValidationMessage For="() => Input.Password" class="mud-input-error"/>
                    </div>
                </div>
            </div>
            <div Class="d-flex justify-space-between align-center mb-1">
                <label class="form-label">
                    <InputCheckbox @bind-Value="Input.RememberMe" class="form-check-input"/>
                    @L["Remember me"]
                </label>
                <MudLink Href="@Forgot.PageUrl">@L["Forgot password?"]</MudLink>
            </div>

            <MudButton Variant="Variant.Filled"
                       Color="Color.Primary"
                       Size="Size.Large"
                       ButtonType="ButtonType.Submit"
                       FullWidth="true">
                <MudText>@L["Sign In"]</MudText>
            </MudButton>
        </EditForm>

    </div>
    <div class="d-flex flex-column">

        <ExternalLoginPicker/>

    </div>
</div>

@code {
    public const string PageUrl = "/pages/authentication/login";
    private string? errorMessage;
    [SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
    [CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
    private string _title = "Sign In";
    private AuthenticationScheme[] externalLogins = [];

    [SupplyParameterFromForm]
    private InputModel Input { get; set; } = new()
    {
        UserName = "administrator",
        Password = "Password123!",
        RememberMe = true
    };

    protected override async Task OnInitializedAsync()
    {
        _title = L["Sign In"];
        if (HttpContext is not null && HttpMethods.IsGet(HttpContext.Request.Method))
        {
            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
        }
    }

    private void RedirectTo(string redirectionUrl)
    {
        ArgumentNullException.ThrowIfNull(HttpContext);
        HttpContext.Response.Headers.Append("blazor-enhanced-nav-redirect-location", redirectionUrl);
        HttpContext.Response.StatusCode = 200;
    }
    public async Task OnSubmit()
    {
        // Check if the user exists
        var user = await UserManager.FindByNameAsync(Input.UserName);
        if (user == null)
        {
            errorMessage = L["Error: User does not exist."];
            return;
        }
        else if(!user.IsActive)
        {
            errorMessage = L["Error: Your account is inactive. Please contact support."];
            return; 
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true
        var result = await SignInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, true);
        if (result.Succeeded)
        {
            // Set the language cookie if user.LanguageCode is not null or empty
            Logger.LogInformation("{UserName} has logged in.",Input.UserName);
            // Determine the language code (use user's language preference or a default value)
            string languageCode = user.LanguageCode ?? "en-US"; 
            // Redirect to the desired URL with the culture as a query parameter
            string redirectUrl = $"{ReturnUrl ?? "/"}?culture={languageCode}";
            RedirectTo(redirectUrl);
        }
        else if (result.RequiresTwoFactor)
        {
          
            var factorUser = (await SignInManager.GetTwoFactorAuthenticationUserAsync()) ?? throw new InvalidOperationException("Unable to load two-factor authentication user.");
            var token = await UserManager.GenerateTwoFactorTokenAsync(factorUser, "Email");
            await Mediator.Publish(new SendFactorCodeNotification(factorUser.Email ?? string.Empty, factorUser.UserName ?? string.Empty, token));
            RedirectManager.RedirectTo(LoginWith2fa.PageUrl, new Dictionary<string, object?> { ["returnUrl"] = ReturnUrl, ["rememberMe"] = Input.RememberMe });
        }
        else if (result.IsLockedOut)
        {
            Logger.LogWarning("{UserName} has locked out.",Input.UserName);
            RedirectManager.RedirectTo(Lockout.PageUrl);
        }
        else if (result.IsNotAllowed)
        {
            errorMessage = L["Error: Your account is not allowed to log in. Please ensure your account has been activated and you have completed all required steps."];
        }
        else
        {
            errorMessage = L["Error: Invalid login attempt."];
        }
    }

    public sealed class InputModel
    {
        [Required(ErrorMessage = "User name cannot be empty")]
        [StringLength(100, ErrorMessage = "Name length can't be more than 100.")]
        public string UserName { get; set; } = "";

        [Required(ErrorMessage = "Password cannot be empty")]
        [StringLength(30, ErrorMessage = "Password must be at least 6 characters long.", MinimumLength = 6)]
        public string Password { get; set; } = "";

        public bool RememberMe { get; set; } = true;
    }

}